#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/qnetworkaccessmanager.h>
#include <QtNetwork/QNetworkReply>
#include <QtNetwork/QNetworkRequest>
#include <QMessageBox>
#include <QInputDialog>
#include <QPainter>
#include <QtXml/QDomDocument>
#include <QIODevice>
#include <QLabel>
#include <math.h>
#include <phonon/AudioOutput>
#include <phonon/MediaObject>
#include <QDir>
#include <QMouseEvent>
#include <QPropertyAnimation>
#include <QPointer>
#include <QSequentialAnimationGroup>
//#include <QVariantAnimation>

//void jmsg( QString );


void JTextAnimation::updateCurrentValue ( const QVariant & value )
{
    QColor qc = value.value<QColor>();
    this->this_palette.setColor(QPalette::WindowText, qc);
    target->setPalette(this_palette);
}

void JLabel::mousePressEvent(QMouseEvent *ev)
{
    ev->accept();
    emit clicked(this->text);
}

QString pwn_strsearch(QString, QString , QString ,QString);

#define pi 3.14159265358979
#define jmsg(txt) msg.setText(txt);msg.exec();

MainWindow::MainWindow(QWidget *parent) :
        QMainWindow(parent),
        ui(new Ui::MainWindow), out(stdout), rounding_p(.05)
{
    this->setWindowFlags(Qt::CustomizeWindowHint);
    qApp->setApplicationName("Wolfpack Xboxlive");

    // Setup config directory
    QString config_path( QDir::homePath() + QDir::separator() + ".qt4xboxlive");
    configdir.setPath(config_path);
    if ( ! configdir.exists() )
    {
        msg.setText("Configuration Directory does not exist\nAttempting to create at " + configdir.absolutePath());
        msg.exec();
        if (! QDir::home().mkdir(config_path) )
            msg.setText("Failed to create configuration directory :-( ");
    }

    // Setup Audio
    soundsdir = QDir(configdir.absolutePath() + QDir::separator() +  "sounds");
    if (! soundsdir.exists())
        configdir.mkdir( "sounds");

    //Initialization of class variables
    netmanager = new QNetworkAccessManager(this);
    info_source=PWN;
    refresh_time_s = 60;

    audio_output = new Phonon::AudioOutput(Phonon::MusicCategory, this);
    media_object = new Phonon::MediaObject(this);
    Phonon::createPath(media_object, audio_output);

    ui->setupUi(this);

    connect( ui->menu_refresh_all , SIGNAL( triggered()), this, SLOT( refresh_all_clicked() ));
    connect( ui->menu_auto_refresh , SIGNAL( triggered()), this, SLOT( auto_refresh_clicked() ));
    connect( ui->menu_source_dmk, SIGNAL( triggered()), this, SLOT( source_dmk_clicked() ));
    connect( ui->menu_source_pwn, SIGNAL( triggered()), this, SLOT( source_pwn_clicked() ));
    connect( &refresh_timer,      SIGNAL( timeout()),   this, SLOT(refresh_all_clicked() ));
    connect( ui->menu_quit,       SIGNAL( triggered()), this, SLOT( quit() ));
    connect( ui->menu_cache,      SIGNAL( triggered()), this, SLOT(cache_clicked()));
    connect( ui->menu_help,       SIGNAL( triggered()), this, SLOT(help_clicked()));
    connect( ui->menu_test,       SIGNAL( triggered()), this, SLOT(test_clicked()));

    QFile gamertags_file(configdir.absoluteFilePath("gamertags"));
    if ( gamertags_file.exists() && gamertags_file.open(QFile::ReadOnly))
    {
        int length;
        do {
            char in_buf[1024];
            length = gamertags_file.readLine(in_buf,sizeof(in_buf));
            if (length > 1 && length <= 16)
                gamertags << QString(in_buf).left(length-1); //-1 to cut off EOL (\n) could cause problems with non linux text files
        } while (length != -1);
        gamertags_file.close();
    }
    else
        gamertags << "FadeToBlack1183"
                << "NecronomiconJon"
                << "La Anaconda" << "La Gioconda"
                << "Ridpath51"
                << "Tei the Tyrant";
    int num_tags = gamertags.length();

    ui->layout_l->setContentsMargins(1, rounding_p  * height(),1,1);
    ui->layout_r->setContentsMargins(1, rounding_p  * height(),1,1);

    for (int index=0; index < num_tags; index++)
    {
        gamercard newcard;

        newcard.lbl_avatar=new JLabel(this);
        newcard.lbl_info  = new QLabel(this);
        newcard.lbl_info->setWordWrap(true);
        newcard.lbl_avatar->setText(gamertags[index]);
        newcard.lbl_avatar->setMouseTracking(true);
        connect( newcard.lbl_avatar, SIGNAL(clicked(QString)), SLOT(avatar_clicked(QString)));
        newcard.gamertag=gamertags[index];
        newcard.online = false;
        newcard.pic_downloaded = false;
        newcard.locked =false;

        ui->layout_l->addWidget(newcard.lbl_avatar);
        ui->layout_r->addWidget(newcard.lbl_info);

        gamercards.append(newcard);
        //get_info(gamercards[index]);
        qApp->processEvents();
    }
    caching=read_cache();
    if (caching)
        read_state_cache();
    ui->menu_cache->setChecked(caching);
}

MainWindow::~MainWindow()
{
    if (caching) write_cache();

    delete media_object;
    delete audio_output;
    delete netmanager;
    for (QList<gamercard>::iterator gc_i=gamercards.begin(); gc_i != gamercards.end(); ++gc_i)
    {  delete gc_i->lbl_info;
        delete gc_i->lbl_avatar;
    }
    delete ui;
}

void MainWindow::get_info(gamercard &gc)
{
    if ( !gc.locked )
    {   gc.locked = true;
        QString info="Not Found", status="", pic_url="", last_game_played_pic_url, temp;
        int  gamerscore=-1;
        bool online=false;


        QPropertyAnimation *animation = new QPropertyAnimation(gc.lbl_avatar, "geometry");
        QRect from, to = gc.lbl_avatar->geometry();
        animation->setDuration(5000);
        from=to;
        from.setHeight(from.height()/2);
        from.setWidth(from.width()/2);
        animation->setStartValue(from);
        animation->setEndValue(to);
        animation->start();//QPropertyAnimation::DeleteWhenStopped);
        QNetworkReply * reply_device;

        switch(info_source)
        {
        case DMK:
            {
                QString gamer_info = "http://xboxapi.duncanmackenzie.net/gamertag.ashx?GamerTag=" + gc.gamertag;
                reply_device = netmanager->get(QNetworkRequest(QUrl(gamer_info)));

                while (! reply_device->isFinished())
                {
                    jslp::msleep(10);
                    qApp->processEvents();
                }
                QByteArray data = reply_device->readAll();
                reply_device->deleteLater();

                QString out = QString(data.mid(21));
                QDomDocument doc ("PresenceInfo");
                doc.setContent( out );
                QDomElement root = doc.documentElement();
                if (root.tagName() == "XboxInfo")
                {
                    QDomElement leaf, leaf2;
                    QDomNode presenceinfo_node = root.namedItem("PresenceInfo");

                    QDomNode info_node = presenceinfo_node.namedItem("Info");
                    leaf = info_node.toElement();
                    QDomNode info2_node = presenceinfo_node.namedItem("Info2");
                    leaf2 = info2_node.toElement();
                    info=leaf.text();
                    if ( ! leaf2.text().isEmpty() )
                        info += " - " + leaf2.text();

                    QDomNode online_node = presenceinfo_node.namedItem("Online");
                    leaf = online_node.toElement();
                    online = leaf.text().compare("true",Qt::CaseInsensitive);

                    QDomNode status_node = presenceinfo_node.namedItem("StatusText");
                    leaf = status_node.toElement();
                    status = leaf.text();

                    QDomNode pic_url_node = root.namedItem("TileUrl");
                    leaf = pic_url_node.toElement();
                    pic_url = leaf.text();

                    QDomNode gamerscore_node = root.namedItem("GamerScore");
                    leaf = gamerscore_node.toElement();
                    temp = leaf.text();
                    gamerscore = temp.toInt();

                    //last game played pic
                    QDomNode recentgames_node = root.namedItem("RecentGames");
                    QDomNode branch1 = recentgames_node.namedItem("XboxUserGameInfo");
                    QDomNode branch2 = branch1.namedItem("Game");
                    QDomNode branch31 = branch2.namedItem("Image64Url");
                    QDomNode branch32 = branch2.namedItem("Name");
                    leaf = branch31.toElement();
                    leaf2= branch32.toElement();
                    last_game_played_pic_url = leaf.text();
                    // it looks like it might be possible to calc a game complete percentage: points/total point, achievement/total achievments
                }
            } break;

        case PWN:
            {
            QString gamer_info = "http://pwndata.appspot.com/widget/data?gamertag=" + gc.gamertag;
            QNetworkRequest request;
            request.setUrl(QUrl(gamer_info));
            request.setRawHeader("User-Agent", "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_5; en-us) AppleWebKit/533.18.1 (KHTML, like Gecko)");
            reply_device = netmanager->get(request);
            while (! reply_device->isFinished())
            {
                qApp->processEvents();
                jslp::msleep(2);
            }
            QByteArray data = reply_device->readAll();

            QString str = QString(data), start, skip_to, stop;
            //gcpic
            start="<div class=\"gcpic\"";
            skip_to=" src='";
            stop="'";
            pic_url = pwn_strsearch( str,  start, skip_to, stop);

            //gcscore
            start="<div class=\"gcscore\"";
            skip_to=">";
            stop="<";
            temp = pwn_strsearch( str,  start, skip_to, stop);
            gamerscore = temp.toInt();

            //gcstatus
            start="<div class=\"gcstatus\"";
            skip_to=">";
            stop="</div>";
            status = pwn_strsearch( str,  start, skip_to, stop);
            online = status.contains("online",Qt::CaseInsensitive);

            //gcinfo
            start = "<div class=\"gcinfo\"";
            skip_to=">";
            stop="<";
            info = pwn_strsearch( str,  start, skip_to, stop);

            //gclastgame (pic)
            start="<div class=\"gclastgame\"";
            skip_to=" src='";
            stop="'";
            last_game_played_pic_url = pwn_strsearch( str,  start, skip_to, stop);
        } break;
        }

        bool diff = false;

        gc.gamerscore = gamerscore;
        gc.info = info;
        gc.status = status;
        gc.last_game_played_url = last_game_played_pic_url;

        gc.lbl_info->setText(gc.gamertag + ": " + status  + "\n" + info);
        if ( (!gc.online) && online)
        {
            playsound(ONLINE,gc.gamertag);
            diff=true;

        }
        gc.online = online;

        if (diff)
        {
            QPalette from_p = gc.lbl_info->palette();
            QColor to_color(QColor(0,255,0,255));
            JTextAnimation * text_anim_1 = new JTextAnimation(gc.lbl_info);
            JTextAnimation * text_anim_2 = new JTextAnimation(gc.lbl_info);

            text_anim_1->setDuration(1000);
            text_anim_2->setDuration(2000);

            text_anim_1->setStartValue(from_p.color(QPalette::WindowText));
            text_anim_1->setEndValue(to_color);
            text_anim_2->setStartValue(to_color);
            text_anim_2->setEndValue(from_p.color(QPalette::WindowText));

            QSequentialAnimationGroup *group = new QSequentialAnimationGroup;
            group->addAnimation(text_anim_1);
            group->addAnimation(text_anim_2);
            group->setLoopCount(10);
            group->start(QAbstractAnimation::DeleteWhenStopped);
        }
        if ( (!gc.pic_downloaded) || (gc.pic_url != pic_url) )
        {
            gc.pic_url = pic_url;
            get_avatar(gc);
        }

        //Stop animation and reset geometry (prevents double click bug)
        animation->stop();
        delete animation;
        gc.lbl_avatar->setGeometry(to);

        gc.locked=false;

        reply_device->deleteLater();
    }

}

void MainWindow::get_avatar(gamercard &gc)
{
    QString gamer_pic_url;
    if (gc.pic_url.isEmpty())
        gamer_pic_url = "http://avatar.xboxlive.com/avatar/" + gc.gamertag + "/avatarpic-l.png";
    else
        gamer_pic_url = gc.pic_url;

    QNetworkReply * reply_device = netmanager->get(QNetworkRequest(QUrl(gamer_pic_url)));

    while (! reply_device->isFinished())
    {
        jslp::msleep(2);
        qApp->processEvents();
    }
    QByteArray data = reply_device->readAll();
    reply_device->deleteLater();
    QPixmap gamer_pic;
    gamer_pic.loadFromData(data);
    gc.lbl_avatar->setPixmap(gamer_pic);
    gc.pic_downloaded = true;
    if (caching)
    {
        QFile cache(configdir.absolutePath() + QDir::separator() + gc.gamertag + ".png");
        cache.open(QFile::WriteOnly);
        cache.write(data);
        cache.close();
    }
}

/*  beginning code for online+offline pic difference
gamer_pic_t;
//QBitmap mask;

if (! gc.online)
{
    double rotation = pi/16;
    double scaling = .7;
    double m11_m22 = cos(rotation) * scaling;
    double m21 = sin(rotation) * scaling;
    double m12 = -m21;
    gamer_pic_t = gamer_pic.transformed(QTransform(m11_m22, m12, m21, m11_m22, 0, 0)); //rotate pi/16
    gamer_pic=gamer_pic_t;
}

//gamer_pic_t = gamer_pic.transformed(QTransform(0.98079, -0.19509, 0.19509, 0.98079, 0, 0)); //rotate pi/16

//gamer_pic_t.setMask();
//mask=QBitmap(gamer_pic_t.size());
//mask.
//gc.lbl_avatar->setText(gamertag);

*/

void MainWindow::refresh_all_clicked()
{
    int num_tags = gamertags.length();
    for (int index=0; index < num_tags; index++)
    {
        get_info(gamercards[index]);
        //get_avatar(gamercards[index]);
    }
}

void MainWindow::auto_refresh_clicked()
{
    bool ok;

    if (ui->menu_auto_refresh->isChecked())
    {
        int temp;
        temp=refresh_time_s;
        temp=QInputDialog::getInt(this,"Refresh time input dialog","Time(s): ", temp, 30, 60*60*24,1,&ok);
        if (ok)
        {
            refresh_time_s = temp;
            refresh_timer.start(refresh_time_s * 1000);
        }
        else
        {
            ui->menu_auto_refresh->setChecked(false);
            refresh_timer.stop();
        }
    }
    else
    {
        refresh_timer.stop();
    }
}

void MainWindow::source_dmk_clicked()
{
    ui->menu_source_dmk->setChecked(true);
    ui->menu_source_pwn->setChecked(false);
    info_source = DMK;
}

void MainWindow::source_pwn_clicked()
{
    ui->menu_source_dmk->setChecked(false);
    ui->menu_source_pwn->setChecked(true);
    info_source = PWN;

}

QString pwn_strsearch(QString str_in, QString start, QString skip_to, QString stop)
{
    int pos1 = str_in.indexOf(start);
    int pos2 = str_in.indexOf(skip_to,pos1+start.length());
    int pos3 = str_in.indexOf(stop,pos2+skip_to.length());
    return str_in.mid(pos2+skip_to.length(),pos3-pos2-skip_to.length());
}

void MainWindow::playsound( SOUNDTYPE t, QString prefix="default")
{
    QString suffix;
    switch  (t) {
    case ONLINE:
        suffix="online";
        break;
    default:
        msg.setText("Not Supported");
        msg.exec();
        break;
    }

    QString testfilepath = soundsdir.absolutePath() + QDir::separator() + prefix + "-" + suffix + ".mp3";
    if (! QFile::exists(testfilepath) )
        testfilepath=soundsdir.absolutePath() + QDir::separator() + "default-" + suffix + ".mp3";

    if (QFile::exists(testfilepath) )
    {
        Phonon::MediaSource source(soundsdir.absolutePath() + QDir::separator() + "default-online.mp3");
        if (source.type() == Phonon::MediaSource::Invalid)
        {    msg.setText("invalid"); msg.exec();}
        else
        {
            media_object->setCurrentSource(source);
            media_object->play();
        }
    }

    /* //Resource crawling
    QDir j(":/sounds/");
    QStringList jj = j.entryList();
    for (int i=0; i<jj.count(); i++)
    {   msg.setText(jj[i]);
        msg.exec();
    }
    */
}

void MainWindow::quit()
{
    qApp->quit();
}

void MainWindow::cache_clicked()
{
    if (ui->menu_cache->isChecked())
        caching = write_cache();
    else caching = false;
}

bool MainWindow::write_cache()
{
    QString filename;
    for (QList<gamercard>::iterator gc_i=gamercards.begin(); gc_i != gamercards.end(); ++gc_i)
    {
        filename = "cache_" + gc_i->gamertag;
        QFile cache_file( configdir.absolutePath() + QDir::separator() + filename);
        if (cache_file.open(QFile::WriteOnly))
        {
            QDataStream out(&cache_file);
            out << gc_i->info << gc_i->status << gc_i->pic_url << gc_i->last_game_played_url
                    << gc_i->online << gc_i->pic_downloaded << gc_i->gamerscore;
            cache_file.close();
        } else return false;
    }

    filename = "state";
    QFile cache_file( configdir.absolutePath() + QDir::separator() + filename);
    if (cache_file.open(QFile::WriteOnly))
    {
        QDataStream out(&cache_file);
        out << this->geometry() << this->palette();
        cache_file.close();
    }

    filename = "gamertags";
    QFile gamertag_file( configdir.absolutePath() + QDir::separator() + filename);
    if (gamertag_file.open(QFile::WriteOnly))
    {
        QTextStream out(&gamertag_file);
        for (QList<gamercard>::iterator gc_i=gamercards.begin(); gc_i != gamercards.end(); ++gc_i)
        {
            out << gc_i->gamertag << "\n";
        }
        gamertag_file.close();

    }
    return true;
}

bool MainWindow::read_cache()
{
    bool found_any_cache=false;
    for (QList<gamercard>::iterator gc_i=gamercards.begin(); gc_i != gamercards.end(); ++gc_i)
    {
        //info cache
        QString filename = "cache_" + gc_i->gamertag;
        QFile cache_file( configdir.absolutePath() + QDir::separator() + filename);
        if (cache_file.exists() && cache_file.open(QFile::ReadOnly))
        {
            QDataStream in(&cache_file);
            in >> gc_i->info >> gc_i->status >> gc_i->pic_url >> gc_i->last_game_played_url
                    >> gc_i->online >> gc_i->pic_downloaded >> gc_i->gamerscore;
            cache_file.close();
            found_any_cache = true;
            gc_i->lbl_info->setText(gc_i->gamertag + ": " + gc_i->status  + "\n" + gc_i->info);
        }

        //avatar cache
        QFile cache(configdir.absolutePath() + QDir::separator() + gc_i->gamertag + ".png");
        if ( cache.exists() && cache.open(QFile::ReadOnly))
        {
            QByteArray data = cache.readAll();
            cache.close();
            QPixmap gamer_pic;
            gamer_pic.loadFromData(data);
            gc_i->lbl_avatar->setPixmap(gamer_pic);
            gc_i->pic_downloaded = true;
        } else gc_i->pic_downloaded = false;
    }
    return found_any_cache;
}

void MainWindow::read_state_cache()
{
    //state cache
    QFile cache_file( configdir.absolutePath() + QDir::separator() + "state");
    if (cache_file.exists() && cache_file.open(QFile::ReadOnly))
    {
        QDataStream in(&cache_file);
        QRect lastgeometry; QPalette lastpalette;
        in >> lastgeometry >> lastpalette;
        this->setGeometry(lastgeometry);
        this->setPalette(lastpalette);
        cache_file.close();
    }
}

void MainWindow::avatar_clicked(const QString & txt)
{
    static bool locked=false;
    if (!locked)
    { locked=true;
        for (QList<gamercard>::iterator gc_i=gamercards.begin(); gc_i != gamercards.end(); ++gc_i)
        {
            if (txt == gc_i->gamertag)
            {    get_info( *gc_i );
                locked=false;
                return;
            }
        }
    }
}

void MainWindow::resizeEvent(QResizeEvent *event)
{
    QRegion maskedRegion(0,0,width(),height());
    QRegion box_ul, box_ur, box_lr,box_ll,box(0,0,width() * rounding_p, height() * rounding_p,QRegion::Rectangle);

    box_ll=box - QRegion(0,-rounding_p*height(), rounding_p*2 * width(), rounding_p*2 *height(),QRegion::Ellipse);
    box_ll.translate(0,height()*(1-rounding_p)+1);

    box_lr=box - QRegion(-rounding_p*width(),-rounding_p*height(), rounding_p*2 * width(), rounding_p*2 *height(),QRegion::Ellipse);
    box_lr.translate(width()*(1-rounding_p)+1,height()*(1-rounding_p)+1);

    box_ul=box - QRegion(0,0, rounding_p*2 * width(), rounding_p*2 *height(),QRegion::Ellipse);

    box_ur=box - QRegion(-rounding_p*width(),0, rounding_p*2 * width(), rounding_p*2 *height(),QRegion::Ellipse);
    box_ur.translate(width()*(1-rounding_p)+1,0);

    maskedRegion-=(box_ll + box_lr + box_ul + box_ur);
    setMask(maskedRegion);
    event->accept();
}

void MainWindow::help_clicked()
{
    msg.setText("Click on a name to refresh it. \n Important Files: \n\t "
                + configdir.absolutePath() + QDir::separator() + "gamertags\n\t "
                + configdir.absolutePath() + QDir::separator() + "sounds" + QDir::separator() + "default-online.[wav|mp3]\n"
                + "\nVersion: " + QString::number(JVERSION) );
    msg.exec();
}

QColor randQColor()
{
    int r=static_cast<double> (qrand()) * 255 / RAND_MAX;
    int g=static_cast<double> (qrand()) * 255 / RAND_MAX;
    int b=static_cast<double> (qrand()) * 255 / RAND_MAX;
    int a=static_cast<double> (qrand()) * 126 / RAND_MAX + 127;
    return QColor(r,g,b,a);
}

void MainWindow::mousePressEvent(QMouseEvent* event)
{
    if(event->button() == Qt::LeftButton)
    {
        mMoving = true;
        mLastMousePosition = event->globalPos();
    }
    else if (event->button() == Qt::RightButton)
    {
        QPalette p=this->palette();
        p.setColor(QPalette::Window,randQColor());
        this->setPalette(p);
    }
}

void MainWindow::mouseMoveEvent(QMouseEvent* event)
{
    if( mMoving && event->buttons().testFlag(Qt::LeftButton) )
    {
        this->move( this->pos() + event->globalPos() - mLastMousePosition);
        mLastMousePosition = event->globalPos();
    }
}

void MainWindow::mouseReleaseEvent(QMouseEvent* event)
{
    if(mMoving && event->button() == Qt::LeftButton)
        mMoving = false;
}

void MainWindow::test_clicked()
{
    msg.setText("here");
    QString login_url="https://login.live.com/login.srf?wa=wsignin1.0&rpsnv=11&ct=1292538036&rver=6.0.5286.0&wp=MBI&wreply=https:%2F%2Flive.xbox.com:443%2Fxweb%2Flive%2Fpassport%2FsetCookies.ashx%3Frru%3Dhttp%253a%252f%252flive.xbox.com%252fen-US%252fMyXbox&lc=1033&cb=reason%3D0%26returnUrl%3Dhttp%253a%252f%252flive.xbox.com%252fen-US%252fMyXbox&id=66262";
    login_url="https://login.live.com/";
    QNetworkReply * reply_device = netmanager->get(QNetworkRequest(QUrl(login_url)));

    while (! reply_device->isFinished())
    {
        jslp::msleep(10);
        qApp->processEvents();
    }
    QByteArray data = reply_device->readAll();
    reply_device->deleteLater();

    QString filename = "debug";
    QFile cache_file( configdir.absolutePath() + QDir::separator() + filename);
    if (cache_file.open(QFile::WriteOnly))
    {
        QDataStream out(&cache_file);
        out << data;
        cache_file.close();
    }
}
